import os
import cv2
import numpy as np
import torch
import torch.nn as nn
from deep_sort_realtime.deepsort_tracker import DeepSort
from detector import PersonDetector

class PersonTracker:
    def __init__(self, detector, max_age=30, n_init=3):
        """
        初始化行人跟踪器
        
        参数:
            detector: PersonDetector对象
            max_age: 跟踪器最大保持年龄
            n_init: 确认跟踪所需的连续检测次数
        """
        self.detector = detector
        self.tracker = DeepSort(
            max_age=max_age,
            n_init=n_init,
            embedder_gpu=True if torch.cuda.is_available() else False
        )
        
        # 包装 DeepSORT 的 update_tracks 方法以处理可能的异常
        self._original_update_tracks = self.tracker.update_tracks
        self.tracker.update_tracks = self._safe_update_tracks
        
        self.tracks = {}  # 存储轨迹信息 {track_id: [(frame, x, y, w, h), ...]}
        
        # 确保修复 'Upsample' object has no attribute 'recompute_scale_factor' 错误
        # 注意：如果detector已经处理过，这里不会重复处理
        for m in self.detector.model.modules():
            if isinstance(m, nn.Upsample):
                m.recompute_scale_factor = None
        
    def _safe_update_tracks(self, detections, frame=None):
        """
        DeepSORT update_tracks 方法的安全包装器
        
        参数:
            detections: 检测结果列表 [(x1, y1, x2, y2, conf), ...] 或 [[[x1,y1,x2,y2], conf], ...]
            frame: 当前视频帧
            
        返回:
            tracks: 跟踪结果列表
        """
        try:
            # 确保 detections 是列表或数组且非空
            if not isinstance(detections, (list, np.ndarray)) or len(detections) == 0:
                print("警告: 没有有效的检测结果")
                return []
            
            # 检查输入格式并转换为 DeepSORT 期望的格式
            valid_detections = []
            for det in detections:
                try:
                    # 检查是否已经是 DeepSORT 期望的格式 [[x1,y1,x2,y2], conf]
                    if (isinstance(det, (list, tuple, np.ndarray)) and 
                        len(det) == 2 and 
                        isinstance(det[0], (list, tuple, np.ndarray)) and 
                        len(det[0]) == 4 and 
                        isinstance(det[1], (int, float))):
                        
                        # 已经是正确的格式
                        xyxy = [float(det[0][0]), float(det[0][1]), float(det[0][2]), float(det[0][3])]
                        conf = float(det[1])
                        valid_detections.append([xyxy, conf])
                    
                    # 检查是否是旧格式 [x1,y1,x2,y2,conf]
                    elif isinstance(det, (list, tuple, np.ndarray)) and len(det) >= 5:
                        # 确保所有值都是数字
                        if all(isinstance(x, (int, float)) for x in det[:5]):
                            # 转换为 DeepSORT 期望的格式
                            xyxy = [float(det[0]), float(det[1]), float(det[2]), float(det[3])]
                            conf = float(det[4])
                            valid_detections.append([xyxy, conf])
                        else:
                            print(f"警告: 检测结果包含非数字值 {det}")
                    else:
                        print(f"警告: 检测结果格式无效 {det}")
                except Exception as e:
                    print(f"处理检测结果时出错: {e}")
                    continue
            
            if not valid_detections:
                print("警告: 没有有效的检测结果")
                return []
            
            # 调用原始的 update_tracks 方法
            return self._original_update_tracks(valid_detections, frame=frame)
        except TypeError as e:
            # 专门处理 'float' has no len() 错误
            if "object of type 'float' has no len()" in str(e):
                print("捕获到 'float' has no len() 错误，可能是 DeepSORT 内部问题")
                
                # 尝试跟踪库的内部代码进行调试
                import traceback
                traceback.print_exc()
                
                # 尝试修复 - 打印更多的调试信息
                print(f"原始检测结果: {detections}")
                
                # 创建一个更规范的 DeepSORT 格式
                try:
                    # 确保每个检测结果都完全符合 DeepSORT 的期望
                    fixed_detections = []
                    
                    # 递归地检查检测结果
                    def fix_detection(det):
                        if isinstance(det, (list, tuple, np.ndarray)):
                            if len(det) == 2 and isinstance(det[1], (int, float)):
                                # 如果det是[xyxy, conf]格式
                                if isinstance(det[0], (list, tuple, np.ndarray)) and len(det[0]) == 4:
                                    return [[float(det[0][0]), float(det[0][1]), float(det[0][2]), float(det[0][3])], float(det[1])]
                                # 如果det是非标准格式但可能能转换
                                elif isinstance(det[0], (int, float)) and len(det) >= 5:
                                    return [[float(det[0]), float(det[1]), float(det[2]), float(det[3])], float(det[4])]
                            elif len(det) >= 5:
                                # 如果det是[x1,y1,x2,y2,conf]格式
                                return [[float(det[0]), float(det[1]), float(det[2]), float(det[3])], float(det[4])]
                        # 不能修复
                        return None
                    
                    for det in detections:
                        fixed_det = fix_detection(det)
                        if fixed_det:
                            fixed_detections.append(fixed_det)
                    
                    if fixed_detections:
                        print(f"尝试使用修复后的检测结果格式: {fixed_detections}")
                        return self._original_update_tracks(fixed_detections, frame=frame)
                except Exception as fix_e:
                    print(f"修复检测结果格式时出错: {fix_e}")
                
                # 如果都失败了，返回空列表
                print("无法修复，返回空结果")
                return []
            else:
                # 其它类型错误
                print(f"DeepSORT update_tracks 出错: {e}")
                return []
        except Exception as e:
            print(f"DeepSORT update_tracks 出错: {e}")
            # 出错时返回空列表
            return []
    
    def update(self, frame, frame_idx):
        """
        更新跟踪器状态
        
        参数:
            frame: 当前视频帧
            frame_idx: 帧索引
            
        返回:
            tracks: 跟踪结果列表 [track_id, x1, y1, x2, y2, conf]
        """
        # 检测人
        detections = self.detector.detect(frame)
        
        # 确保 detections 是数组对象
        if not isinstance(detections, np.ndarray):
            print(f"警告: detections 不是 numpy 数组，而是 {type(detections)}")
            # 如果是浮点数或其他标量类型，返回空列表
            if isinstance(detections, (int, float)):
                return []
            # 尝试转换为 numpy 数组
            try:
                detections = np.array(detections)
            except:
                print(f"无法将 detections 转换为数组，返回空列表")
                return []
        
        # 如果没有检测到人，返回空列表
        if len(detections) == 0:
            return []
        
        # 准备DeepSORT输入格式 (x1, y1, x2, y2, conf)
        detection_boxes = []
        for det in detections:
            try:
                x1, y1, x2, y2, conf, _ = det
                # DeepSORT期望的格式是 [[x1,y1,x2,y2], confidence]
                xyxy = [float(x1), float(y1), float(x2), float(y2)]
                detection_boxes.append([xyxy, float(conf)])
            except Exception as e:
                print(f"处理检测框时出错: {e}")
                continue
        
        # 如果没有有效的检测框，返回空列表
        if not detection_boxes:
            return []
        
        # 调试输出，确认格式是否正确
        if detection_boxes and len(detection_boxes) > 0:
            print(f"检测框示例: {detection_boxes[0]}")
            
        # 更新跟踪器 - 使用我们的安全包装器
        # 不需要额外的try-except，因为_safe_update_tracks已经处理了异常
        tracks = self.tracker.update_tracks(detection_boxes, frame=frame)
        
        results = []
        for track in tracks:
            try:
                if not track.is_confirmed():
                    continue
                
                track_id = track.track_id
                # 确保 track_id 是整数
                track_id = int(track_id)
                
                # 获取边界框并进行安全类型转换
                try:
                    ltrb = track.to_ltrb()
                    # 检查 ltrb 是否是有效的值
                    if isinstance(ltrb, (int, float)):
                        print(f"警告: ltrb 是标量值 {ltrb}，跳过此跟踪")
                        continue
                    
                    # 如果 ltrb 是列表或元组，将其拆包
                    if isinstance(ltrb, (list, tuple, np.ndarray)) and len(ltrb) >= 4:
                        x1, y1, x2, y2 = ltrb
                        # 确保边界框坐标是浮点数
                        x1, y1, x2, y2 = float(x1), float(y1), float(x2), float(y2)
                    else:
                        print(f"警告: ltrb 的类型不支持: {type(ltrb)}, 值: {ltrb}")
                        continue
                except Exception as e:
                    print(f"获取边界框时出错: {e}")
                    continue
                
                # 跟踪结果 [track_id, x1, y1, x2, y2, conf]
                results.append([track_id, x1, y1, x2, y2, 1.0])
                
                # 记录轨迹信息
                if track_id not in self.tracks:
                    self.tracks[track_id] = []
                    
                # 计算中心点
                center_x = (x1 + x2) / 2
                center_y = (y1 + y2) / 2
                width = x2 - x1
                height = y2 - y1
                
                # 添加到轨迹历史
                self.tracks[track_id].append((frame_idx, center_x, center_y, width, height))
            except Exception as e:
                print(f"处理跟踪结果时出错: {e}")
                continue
        
        return results
    
    def verify_video_file(self, video_path):
        """
        验证视频文件是否可以被OpenCV正确读取
        
        参数:
            video_path: 视频文件路径
            
        返回:
            is_valid: 视频是否可以被读取
            info: 视频信息字典
        """
        if not os.path.exists(video_path):
            return False, {"error": "文件不存在"}
        
        if os.path.getsize(video_path) == 0:
            return False, {"error": "文件大小为0"}
        
        try:
            cap = cv2.VideoCapture(video_path)
            if not cap.isOpened():
                return False, {"error": "无法打开视频文件"}
            
            # 获取视频信息
            width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            fps = cap.get(cv2.CAP_PROP_FPS)
            frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
            
            # 尝试读取第一帧
            ret, frame = cap.read()
            if not ret:
                cap.release()
                return False, {"error": "无法读取第一帧"}
            
            # 尝试随机读取一帧（如果有多个帧）
            if frame_count > 10:
                random_frame = min(10, frame_count-1)
                cap.set(cv2.CAP_PROP_POS_FRAMES, random_frame)
                ret, frame = cap.read()
                if not ret:
                    cap.release()
                    return False, {"error": f"无法读取第 {random_frame} 帧"}
            
            # 关闭视频
            cap.release()
            
            return True, {
                "width": width,
                "height": height,
                "fps": fps,
                "frame_count": frame_count,
                "file_size_mb": os.path.getsize(video_path) / (1024*1024)
            }
        except Exception as e:
            return False, {"error": f"验证视频文件时出错: {str(e)}"}

    def ensure_playable_video(self, output_path):
        """
        确保输出的视频文件可播放，必要时进行修复
        
        参数:
            output_path: 输出视频路径
            
        返回:
            fixed_path: 修复后的视频路径（如果修复成功）或原始路径
        """
        if not output_path or not os.path.exists(output_path):
            print(f"视频文件不存在: {output_path}")
            return output_path
        
        try:
            # 验证视频
            is_valid, info = self.verify_video_file(output_path)
            
            if is_valid:
                print(f"视频文件验证通过: {output_path}, {info}")
                
                # 检查视频编码是否已经是H.264
                try:
                    import subprocess
                    codec_cmd = ['ffprobe', '-v', 'error', '-select_streams', 'v:0', 
                            '-show_entries', 'stream=codec_name', '-of', 'default=noprint_wrappers=1:nokey=1', 
                            output_path]
                    
                    result = subprocess.run(codec_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
                    codec = result.stdout.strip()
                    
                    if codec and codec.lower() in ['h264', 'avc1', 'x264']:
                        print(f"视频已经是H.264编码 ({codec})，无需转换")
                        return output_path
                    else:
                        print(f"视频编码为 {codec}，需要转换为H.264")
                except Exception as e:
                    print(f"检查视频编码时出错: {e}")
                
                return output_path
            
            print(f"视频文件需要修复: {output_path}, {info}")
            
            # 生成临时文件名
            base_dir = os.path.dirname(output_path)
            filename = os.path.basename(output_path)
            name, ext = os.path.splitext(filename)
            fixed_path = os.path.join(base_dir, f"{name}_fixed{ext}")
            
            # 尝试使用FFmpeg修复（如果系统中安装了FFmpeg）
            try:
                import subprocess
                print(f"尝试使用FFmpeg修复视频，转换为高质量H.264格式...")
                # 使用FFmpeg将视频重新编码为H.264格式，使用高质量设置
                cmd = [
                    'ffmpeg', '-y', '-i', output_path,
                    '-c:v', 'libx264',                  # H.264编码器
                    '-preset', 'slow',                  # 慢速预设，提高质量
                    '-profile:v', 'high',               # 高级编码配置文件
                    '-level', '4.1',                    # 兼容级别，适合大多数设备
                    '-crf', '18',                       # 恒定质量因子（18-28是好的值，越低质量越高）
                    '-pix_fmt', 'yuv420p',              # 确保兼容大多数播放器
                    '-movflags', '+faststart',          # 优化Web播放
                    '-bf', '2',                         # B帧设置
                    '-g', '30',                         # GOP大小
                    '-keyint_min', '15',                # 最小关键帧间隔
                    '-sc_threshold', '40',              # 场景变化阈值
                    '-an',                              # 去除音频（如果不需要）
                    fixed_path
                ]
                
                process = subprocess.Popen(
                    cmd, 
                    stdout=subprocess.PIPE, 
                    stderr=subprocess.PIPE
                )
                stdout, stderr = process.communicate()
                
                if process.returncode == 0 and os.path.exists(fixed_path) and os.path.getsize(fixed_path) > 0:
                    print(f"FFmpeg修复成功，转换为高质量H.264: {fixed_path}")
                    print(f"修复后视频大小: {os.path.getsize(fixed_path) / (1024*1024):.2f} MB")
                    return fixed_path
                else:
                    print(f"高质量H.264转换失败，返回代码: {process.returncode}")
                    print(f"错误信息: {stderr.decode('utf-8', errors='ignore')}")
                    
                    # 尝试使用更简单的命令
                    print("尝试使用简化的H.264转换命令...")
                    simple_cmd = [
                        'ffmpeg', '-y', '-i', output_path,
                        '-c:v', 'libx264',               # H.264编码器
                        '-preset', 'medium',             # 平衡速度和质量
                        '-profile:v', 'baseline',        # 基础配置，最大兼容性
                        '-level', '3.0',                 # 兼容级别
                        '-crf', '23',                    # 中等质量
                        '-pix_fmt', 'yuv420p',           # 确保兼容大多数播放器
                        '-movflags', '+faststart',       # 优化Web播放
                        '-an',                           # 去除音频
                        fixed_path
                    ]
                    
                    process = subprocess.Popen(
                        simple_cmd,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE
                    )
                    stdout, stderr = process.communicate()
                    
                    if process.returncode == 0 and os.path.exists(fixed_path) and os.path.getsize(fixed_path) > 0:
                        print(f"使用简化命令成功转换为H.264: {fixed_path}")
                        return fixed_path
                    else:
                        print(f"所有H.264转换尝试均失败，将继续尝试使用OpenCV: {stderr.decode('utf-8', errors='ignore')}")
            except Exception as e:
                print(f"调用FFmpeg出错: {e}")
            
            # 回退：尝试使用OpenCV修复
            try:
                print(f"尝试使用OpenCV修复视频...")
                cap = cv2.VideoCapture(output_path)
                if not cap.isOpened():
                    print(f"无法使用OpenCV打开视频文件")
                    return output_path
                
                # 获取视频信息
                width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
                height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
                fps = cap.get(cv2.CAP_PROP_FPS)
                
                # 创建视频写入器 - 尝试使用H.264编码器
                try:
                    print("尝试使用H.264编码器(avc1)...")
                    fourcc = cv2.VideoWriter_fourcc(*'avc1')
                    out = cv2.VideoWriter(fixed_path, fourcc, fps, (width, height))
                    
                    if not out.isOpened():
                        print("尝试使用H.264编码器(H264)...")
                        fourcc = cv2.VideoWriter_fourcc(*'H264')
                        out = cv2.VideoWriter(fixed_path, fourcc, fps, (width, height))
                    
                    if not out.isOpened():
                        print("回退到mp4v编码器...")
                        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
                        out = cv2.VideoWriter(fixed_path, fourcc, fps, (width, height))
                except Exception as e:
                    print(f"设置编码器出错: {e}")
                    # 使用默认编码器
                    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
                    out = cv2.VideoWriter(fixed_path, fourcc, fps, (width, height))
                
                # 逐帧复制
                frame_count = 0
                while True:
                    ret, frame = cap.read()
                    if not ret:
                        break
                    
                    out.write(frame)
                    frame_count += 1
                    
                    if frame_count % 500 == 0:
                        print(f"已处理 {frame_count} 帧")
                
                # 释放资源
                cap.release()
                out.release()
                
                # 验证修复后的视频
                is_valid, info = self.verify_video_file(fixed_path)
                if is_valid and os.path.getsize(fixed_path) > 0:
                    print(f"OpenCV修复成功: {fixed_path}, {info}")
                    return fixed_path
                else:
                    print(f"OpenCV修复失败: {info}")
            except Exception as e:
                print(f"OpenCV修复视频出错: {e}")
            
            # 所有修复方法都失败，返回原始路径
            return output_path
        except Exception as e:
            print(f"视频修复过程出错: {e}")
            return output_path

    def track_video(self, video_path, output_path=None, show=False, progress_callback=None):
        """
        对视频进行行人跟踪
        
        参数:
            video_path: 输入视频路径
            output_path: 输出视频路径
            show: 是否实时显示跟踪结果
            progress_callback: 进度回调函数，接收参数为当前进度百分比
            
        返回:
            self.tracks: 所有跟踪的轨迹
        """
        try:
            # 检查输入视频
            print(f"输入视频路径: {video_path}")
            if not os.path.exists(video_path):
                raise ValueError(f"输入视频不存在: {video_path}")
            
            # 检查输出路径
            if output_path:
                print(f"输出视频路径: {output_path}")
                output_dir = os.path.dirname(os.path.abspath(output_path))
                print(f"输出目录: {output_dir}")
                
                # 检查输出目录是否可写
                if not os.path.exists(output_dir):
                    print(f"输出目录不存在，尝试创建: {output_dir}")
                    os.makedirs(output_dir, exist_ok=True)
                
                if not os.access(output_dir, os.W_OK):
                    print(f"警告: 输出目录可能没有写入权限: {output_dir}")
            
            # 打开视频
            cap = cv2.VideoCapture(video_path)
            if not cap.isOpened():
                raise ValueError(f"无法打开视频: {video_path}")
            
            # 获取视频信息
            width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            fps = cap.get(cv2.CAP_PROP_FPS)
            total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
            
            print(f"视频信息: 宽度={width}, 高度={height}, FPS={fps}, 总帧数={total_frames}")
            
            # 设置输出视频
            out = None
            if output_path:
                try:
                    # 确保输出目录存在
                    os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
                    
                    # 直接使用H.264编码器
                    # 注意：不同系统上的H.264编码器名称可能不同
                    try:
                        # 优先尝试兼容性最好的H.264编码器
                        print("尝试使用H.264编码器(avc1)...")
                        fourcc = cv2.VideoWriter_fourcc(*'avc1')  # 使用H.264
                        out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
                        
                        if not out.isOpened():
                            print("尝试使用H.264编码器(H264)...")
                            fourcc = cv2.VideoWriter_fourcc(*'H264')  # 另一种H.264编码器名称
                            out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
                        
                        if not out.isOpened():
                            print("尝试使用H.264编码器(X264)...")
                            fourcc = cv2.VideoWriter_fourcc(*'X264')  # 又一种H.264编码器名称
                            out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
                            
                        # 如果仍然失败，回退到mp4v
                        if not out.isOpened():
                            print("尝试使用mp4v编码器...")
                            fourcc = cv2.VideoWriter_fourcc(*'mp4v')
                            out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
                            
                            # 最后尝试XVID
                            if not out.isOpened():
                                print("尝试使用XVID编码器...")
                                fourcc = cv2.VideoWriter_fourcc(*'XVID')
                                out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
                                
                    except Exception as e:
                        print(f"设置编码器出错: {e}")
                        # 直接使用默认编码器
                        try:
                            print("尝试使用默认编码器...")
                            out = cv2.VideoWriter(output_path, -1, fps, (width, height))
                        except Exception as e2:
                            print(f"使用默认编码器也失败: {e2}")
                            out = None
                    
                    if not out.isOpened():
                        print(f"警告: 无法创建输出视频文件 {output_path}，所有编码器都失败了")
                        out = None
                    else:
                        print(f"成功创建输出视频: {output_path}")
                except Exception as e:
                    print(f"创建输出视频时出错: {e}")
                    out = None
            
            # 重置轨迹信息
            self.tracks = {}
            
            # 处理每一帧
            frame_idx = 0
            
            while True:
                try:
                    ret, frame = cap.read()
                    if not ret:
                        break
                    
                    # 更新跟踪器
                    try:
                        tracks = self.update(frame, frame_idx)
                    except Exception as e:
                        print(f"在帧 {frame_idx} 更新跟踪器时出错: {e}")
                        tracks = []
                    
                    # 绘制跟踪结果
                    for track in tracks:
                        try:
                            # 检查 track 是否有足够的元素
                            if len(track) < 6:
                                print(f"警告: 跟踪结果格式无效，元素数量不足: {track}")
                                continue
                                
                            track_id, x1, y1, x2, y2, conf = track
                            
                            # 确保所有值都是正确的类型
                            track_id = int(track_id)
                            x1, y1, x2, y2 = float(x1), float(y1), float(x2), float(y2)
                            
                            # 随机颜色 (固定每个ID的颜色)
                            color = ((track_id * 123) % 255, (track_id * 45) % 255, (track_id * 67) % 255)
                            
                            # 绘制边界框
                            cv2.rectangle(frame, 
                                         (int(x1), int(y1)), 
                                         (int(x2), int(y2)), 
                                         color, 2)
                            
                            # 绘制ID
                            label = f"ID: {track_id}"
                            # 确保所有参数类型正确
                            try:
                                text_x = int(x1)
                                text_y = max(0, int(y1) - 10)
                                font_face = cv2.FONT_HERSHEY_SIMPLEX
                                font_scale = 0.5
                                line_type = 2
                                
                                # 显式使用位置参数，避免格式化错误
                                cv2.putText(
                                    img=frame,
                                    text=label,
                                    org=(text_x, text_y),
                                    fontFace=font_face,
                                    fontScale=font_scale,
                                    color=color,
                                    thickness=line_type
                                )
                            except Exception as e:
                                print(f"绘制ID文本时出错: {str(e)}")
                                # 尝试使用最简单的参数
                                try:
                                    cv2.putText(frame, label, (int(x1), int(y1)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
                                except Exception as e2:
                                    print(f"尝试简化绘制文本也失败: {str(e2)}")
                            
                            # 绘制轨迹
                            if track_id in self.tracks and len(self.tracks[track_id]) > 1:
                                # 只取最近30个点绘制轨迹
                                track_history = self.tracks[track_id][-30:]
                                
                                # 绘制轨迹线
                                for i in range(1, len(track_history)):
                                    try:
                                        # 确保坐标是有效的整数
                                        pt1_x = int(track_history[i-1][1])
                                        pt1_y = int(track_history[i-1][2])
                                        pt2_x = int(track_history[i][1])
                                        pt2_y = int(track_history[i][2])
                                        
                                        # 安全检查坐标范围
                                        pt1_x = max(0, min(pt1_x, frame.shape[1]-1))
                                        pt1_y = max(0, min(pt1_y, frame.shape[0]-1))
                                        pt2_x = max(0, min(pt2_x, frame.shape[1]-1))
                                        pt2_y = max(0, min(pt2_y, frame.shape[0]-1))
                                        
                                        cv2.line(frame, 
                                                (pt1_x, pt1_y), 
                                                (pt2_x, pt2_y), 
                                                color, 2)
                                    except Exception as e:
                                        print(f"绘制轨迹线时出错: {e}, 历史点: {track_history[i-1]}, {track_history[i]}")
                                        continue
                        except Exception as e:
                            print(f"绘制跟踪结果时出错: {str(e)}, 跟踪结果: {track}")
                            continue
                    
                    # 显示进度信息
                    progress = f"Processing: {frame_idx+1}/{total_frames} frames"
                    cv2.putText(frame, progress, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
                    
                    # 保存结果
                    if out is not None:
                        try:
                            # 确保帧格式正确
                            if frame is not None and frame.shape[0] > 0 and frame.shape[1] > 0:
                                # 确保视频帧是8位无符号整数
                                if frame.dtype != np.uint8:
                                    frame = frame.astype(np.uint8)
                                
                                # 检查帧是否有三个通道 (BGR)
                                if len(frame.shape) == 2:  # 灰度图像
                                    frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
                                elif frame.shape[2] == 4:  # RGBA图像
                                    frame = cv2.cvtColor(frame, cv2.COLOR_RGBA2BGR)
                                
                                out.write(frame)
                                
                                # 定期检查视频写入是否正常
                                if frame_idx % 500 == 0:
                                    print(f"成功写入第 {frame_idx} 帧到视频文件")
                            else:
                                print(f"跳过写入空帧或形状不正确的帧: {frame.shape if frame is not None else None}")
                        except Exception as e:
                            print(f"写入视频帧时出错: {e}")
                    
                    # 显示结果
                    if show:
                        try:
                            cv2.imshow('Tracking', frame)
                            if cv2.waitKey(1) & 0xFF == ord('q'):
                                break
                        except Exception as e:
                            print(f"显示视频帧时出错: {e}")
                    
                    frame_idx += 1
                    
                    # 每100帧显示进度
                    if frame_idx % 100 == 0:
                        print(f"已处理 {frame_idx}/{total_frames} 帧")
                    
                    # 调用进度回调函数
                    if progress_callback:
                        progress_callback(frame_idx / total_frames)
                except Exception as e:
                    print(f"处理帧 {frame_idx} 时出错: {e}")
                    frame_idx += 1
                    continue
            
            # 释放资源
            cap.release()
            if out is not None:
                # 确保视频文件被正确关闭
                try:
                    out.release()
                    print(f"视频文件已成功关闭: {output_path}")
                    
                    # 检查文件是否确实存在且大小不为零
                    if os.path.exists(output_path) and os.path.getsize(output_path) > 0:
                        print(f"已确认视频文件存在且非空: {output_path}, 大小: {os.path.getsize(output_path) / (1024*1024):.2f} MB")
                        
                        # 验证视频是否可以被读取
                        is_valid, info = self.verify_video_file(output_path)
                        if is_valid:
                            print(f"输出视频文件验证成功: {info}")
                        else:
                            print(f"警告: 输出视频文件无法被正确读取: {info}")
                    else:
                        print(f"警告: 输出视频文件不存在或为空: {output_path}")
                except Exception as e:
                    print(f"关闭视频文件时出错: {e}")
            
            if show:
                cv2.destroyAllWindows()
            
            print(f"视频处理完成，共 {frame_idx} 帧，跟踪到 {len(self.tracks)} 个目标")
            
            # 验证并确保输出视频可播放
            if output_path and os.path.exists(output_path):
                final_output_path = self.ensure_playable_video(output_path)
                if final_output_path != output_path:
                    print(f"视频文件已修复: {final_output_path}")
                    # 如果是用于网页播放，可能需要更新数据库中的视频路径
            
            return self.tracks
            
        except Exception as e:
            print(f"跟踪视频时出错: {e}")
            # 确保返回一个空字典而不是None，避免后续处理错误
            return {}

    def get_trajectories_data(self):
        """
        获取轨迹数据用于数据库存储和分析
        
        返回:
            trajectories: 轨迹数据列表 [(track_id, frame, x, y, width, height), ...]
        """
        trajectories = []
        
        for track_id, track_data in self.tracks.items():
            for frame, x, y, width, height in track_data:
                trajectories.append({
                    'track_id': track_id,
                    'frame': frame,
                    'x': x,
                    'y': y,
                    'width': width,
                    'height': height
                })
        
        return trajectories

# 测试示例
if __name__ == "__main__":
    detector = PersonDetector()
    tracker = PersonTracker(detector)
    
    # 处理视频
    # tracks = tracker.track_video('test.mp4', 'output_tracked.mp4', show=True)
    # print(f"共跟踪到 {len(tracks)} 个行人") 